#include <stdio.h>
#include <stdint.h>
#include "SDL.h"
#include "sdl_comm.h"
#include "SDL_video.h"

#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/old_pix_fmts.h"

#include "libavcodec/avfft.h"
#include "libavfilter/avfilter.h"
#include "libavdevice/avdevice.h"
#include "libavutil/avutil.h"
#include "libswresample/swresample.h"
#include "libswscale/swscale.h"

SDL_Overlay     *bmp;
SDL_Rect        rect;
extern int quit;

typedef struct PacketQueue{
	AVPacketList *first_pkt,*last_pkt;
	int nb_packets;
	int size;
	SDL_mutex *mutex;
	SDL_cond *cond;
}PacketQueue;

//����BMP�ļ�ͷ
typedef struct tagBITMAPFILEHEADER {
	unsigned  bfType;
	unsigned long bfSize;
	unsigned    bfReserved1;
	unsigned    bfReserved2;
	unsigned long   bfOffBits;
}BITMAPFILEHEADER, *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER{
	unsigned long      biSize;
	long       biWidth;
	long       biHeight;
	unsigned       biPlanes;
	unsigned       biBitCount;
	unsigned long      biCompression;
	unsigned long      biSizeImage;
	long       biXPelsPerMeter;
	long       biYPelsPerMeter;
	unsigned long      biClrUsed;
	unsigned long      biClrImportant;
} BITMAPINFOHEADER, *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;

PacketQueue audioq;

//����BMP�ļ��ĺ���
void SaveAsBMP (AVFrame *pFrameRGB, int width, int height, int index, int bpp)
{
    //char buf[5] = {0};
    BITMAPFILEHEADER bmpheader;
    BITMAPINFOHEADER bmpinfo;
    FILE *fp;

    char filename[255] = {0};
       //�ļ����·��������Լ����޸�
    sprintf(filename,"frame%d.ppm",index);
   if ( (fp=fopen(filename,"wb+")) == NULL )
    {
       printf ("open file failed!\n");
       return;
    }

    bmpheader.bfType = 0x4d42;
    bmpheader.bfReserved1 = 0;
    bmpheader.bfReserved2 = 0;
    bmpheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    bmpheader.bfSize = bmpheader.bfOffBits + width*height*bpp/8;

    bmpinfo.biSize = sizeof(BITMAPINFOHEADER);
    bmpinfo.biWidth = width;
    bmpinfo.biHeight = height;
    bmpinfo.biPlanes = 1;
    bmpinfo.biBitCount = bpp;
    bmpinfo.biCompression = 0;//BI_RGB;
    bmpinfo.biSizeImage = (width*bpp+31)/32*4*height;
    bmpinfo.biXPelsPerMeter = 100;
    bmpinfo.biYPelsPerMeter = 100;
    bmpinfo.biClrUsed = 0;
    bmpinfo.biClrImportant = 0;

    fwrite (&bmpheader, sizeof(bmpheader), 1, fp);
    fwrite (&bmpinfo, sizeof(bmpinfo), 1, fp);
    fwrite (pFrameRGB->data[0], width*height*bpp/8, 1, fp);

    fclose(fp);
}
static void SaveFrame(AVFrame *pFrame,int width,int height,int iFrame){
	FILE *pFile;
	char szFilename[32] = {0};
	//int x;
	int y;
	sprintf(szFilename,"frame%d.ppm",iFrame);
	pFile = fopen(szFilename,"wb");
	if(!pFile)
		return;
	fprintf(pFile,"p6\n%d %d\n255\n",width,height);
	/*
	for(y=0;y < height;y++)
		for(x = 0;x < width;x++){
			if(x > screen->w || y > screen->h)
				goto end;
			putpixel(screen,x,y,pFrame->data[0]+x+pFrame->linesize[0]*y);
	}
	*/
	for(y=0;y<height;y++)
			fwrite(pFrame->data[0]+y*pFrame->linesize[0],1,width*3,pFile);
	fclose(pFile);
	//display_bmp(szFilename);
	//remove(szFilename);
	//end:
	//SDL_UpdateRect(screen,0,0,screen->w,screen->h);
}

struct SwsContext *img_convert_ctx = NULL;

#define SDL_AUDIO_BUFFER_SIZE 1024;


void packet_queue_init(PacketQueue *queue){
	memset(queue,0,sizeof(PacketQueue));
	queue->mutex = SDL_CreateMutex();
	queue->cond = SDL_CreateCond();
}


int packet_queue_put(PacketQueue *q, AVPacket *pkt) {

  AVPacketList *pkt1;
  if(av_dup_packet(pkt) < 0) {
    return -1;
  }
  pkt1 = av_malloc(sizeof(AVPacketList));
  if (!pkt1)
    return -1;
  pkt1->pkt = *pkt;
  pkt1->next = NULL;


  SDL_LockMutex(q->mutex);

  if (!q->last_pkt)
    q->first_pkt = pkt1;
  else
    q->last_pkt->next = pkt1;
  q->last_pkt = pkt1;
  q->nb_packets++;
  q->size += pkt1->pkt.size;
  SDL_CondSignal(q->cond);

  SDL_UnlockMutex(q->mutex);
  return 0;
}

static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) {
  AVPacketList *pkt1;
  int ret;

  SDL_LockMutex(q->mutex);

  for(;;) {

    if(quit) {
      ret = -1;
      break;
    }

    pkt1 = q->first_pkt;
    if (pkt1) {
      q->first_pkt = pkt1->next;
      if (!q->first_pkt)
    q->last_pkt = NULL;
      q->nb_packets--;
      q->size -= pkt1->pkt.size;
      *pkt = pkt1->pkt;
      av_free(pkt1);
      ret = 1;
      break;
    } else if (!block) {
      ret = 0;
      break;
    } else {
      SDL_CondWait(q->cond, q->mutex);
    }
  }
  SDL_UnlockMutex(q->mutex);
  return ret;
}


int audio_decode_frame(AVCodecContext *aCodecCtx, uint8_t *audio_buf,
                       int buf_size) {

  static AVPacket pkt;
  static uint8_t *audio_pkt_data = NULL;
  static int audio_pkt_size = 0;

  int len1, data_size;

  for(;;) {
    while(audio_pkt_size > 0) {
      data_size = buf_size;
      len1 = avcodec_decode_audio3(aCodecCtx, (int16_t *)audio_buf, &data_size,&pkt);
      if(len1 < 0) {
    	  audio_pkt_size = 0;
    	  break;
      }
      audio_pkt_data += len1;
      audio_pkt_size -= len1;

      if(data_size <= 0) {
    	  continue;
      }

      return data_size;
    }
    if(pkt.data)
      av_free_packet(&pkt);

    if(quit) {
      return -1;
    }

    if(packet_queue_get(&audioq, &pkt, 1) < 0) {
      return -1;
    }
    audio_pkt_data = pkt.data;
    audio_pkt_size = pkt.size;
  }
}





void audio_callback(void *userdata, Uint8 *stream, int len) {

  AVCodecContext *aCodecCtx = (AVCodecContext *)userdata;
  int len1, audio_size;

  static uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];
  static unsigned int audio_buf_size = 0;
  static unsigned int audio_buf_index = 0;

  while(len > 0) {
    if(audio_buf_index >= audio_buf_size) {

      audio_size = audio_decode_frame(aCodecCtx, audio_buf,
                                      sizeof(audio_buf));
      if(audio_size < 0) {

    audio_buf_size = 1024;
    memset(audio_buf, 0, audio_buf_size);
      } else {
    audio_buf_size = audio_size;
      }
      audio_buf_index = 0;
    }
    len1 = audio_buf_size - audio_buf_index;
    if(len1 > len)
      len1 = len;
    memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1);
    len -= len1;
    stream += len1;
    audio_buf_index += len1;
  }
}


int audio_test(){
	AVFormatContext *pFormatCtx = NULL;
	int i,audioStream = -1;
	AVCodecContext *pAudioCodexCtx = NULL;
	AVCodec *pAudioCodec = NULL;
	AVPacket packet;
	av_register_all();
	char *file_name = "";
	if(avformat_open_input(&pFormatCtx,file_name,NULL,NULL) != 0){
		return -1;
	}
	if(avformat_find_stream_info(pFormatCtx,NULL) < 0)
		return -1;
	//av_dump_info(pFormatCtx,-1,file_name,0);
	for(i=0;i<pFormatCtx->nb_streams;i++){
		if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO){
			audioStream = i;
			break;
		}
	}
	if(audioStream == -1)
		return -1;
	pAudioCodexCtx = pFormatCtx->streams[audioStream]->codec;
	if(pAudioCodexCtx == NULL)
		return -1;
	pAudioCodec = avcodec_find_decoder(pAudioCodexCtx->codec_id);
	if(pAudioCodec == NULL)
		return -1;

}

int main(int argc,char **argv){

	  //������Ҫ������ļ��ĸ�ʽ��Ϣ���������ĸ����Լ�����ݵ�
	  AVFormatContext *pFormatCtx = NULL;
	  int             i, videoStream;
	  int audioStream = -1;
	  //��������Ӧ������ϸ������Ϣ��������Ƶ�Ŀ?�ߣ��������͵�
	  AVCodecContext  *pCodecCtx = NULL;
	  AVCodecContext  *pAudioCodeCtx = NULL;
	  //����ı�������������б������Ҫ���õĺ���
	  AVCodec         *pCodec = NULL;
	  AVCodec	*pAudioCodec = NULL;
	  //���ڱ������֡����ݽṹ�����������֡�ֱ��Ǳ�����ɫת��ǰ�����֡ͼ��
	  AVFrame         *pFrame = NULL;
	  AVFrame         *pFrameRGB = NULL;
	  //�����ļ�ʱ�Ὣ��/��Ƶ֡���뵽packet��
	  AVPacket        packet;
	  int             frameFinished;
	  int             numBytes;
	  uint8_t         *buffer = NULL;
	  if(cum_SDL_init() == -1){
		  return -1;
	  }
	  //display_bmp("frame23.ppm");
#if 1
	  av_register_all();
	  char *file_name = "/mnt/other_c/f1.flv";
	  //��ͷ�ļ�
	  if(avformat_open_input(&pFormatCtx,file_name,NULL,NULL) != 0){
		  return -1;
	  }
	  //��ȡ�ļ��е�����Ϣ
	  if(avformat_find_stream_info(pFormatCtx,NULL) < 0)
		  return -1;
	  //����ļ�����Ϣ
	  av_dump_format(pFormatCtx,-1,file_name,0);
      //���Ǳ������ҵ���һ����Ƶ��
	  videoStream = -1;
	  for(i = 0;i < pFormatCtx->nb_streams;i++){
		  if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
			  videoStream = i;
			  break;
		  }
	  }

	  for(i = 0;i<pFormatCtx->nb_streams;i++){
		  if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO){
			  audioStream = i;
			  break;
		  }
	  }

	  if(videoStream == -1)
		  return -1;
	  if(audioStream == -1)
		  return -1;

	  pCodecCtx = pFormatCtx->streams[videoStream]->codec;
	  if(pCodecCtx == NULL)
		  return -1;
      //�ҵ����ʵĽ�����
	  pAudioCodeCtx = pFormatCtx->streams[audioStream]->codec;

	  pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
	  if(pCodec == NULL)
		  return -1;

	  pAudioCodec = avcodec_find_decoder(pAudioCodeCtx->codec_id);
	  if(pAudioCodec == NULL)
		  return -1;

	  //�򵽺��ʵĽ�����
	  if(avcodec_open2(pCodecCtx,pCodec,NULL) < 0)
		  return -1;

	  if(avcodec_open2(pAudioCodeCtx,pAudioCodec,NULL) < 0)
		  return -1;

	  SDL_AudioSpec audioSpec = {0},spec = {0};
	  audioSpec.freq = pAudioCodeCtx->sample_rate;
	  audioSpec.format = AUDIO_S16SYS;
	  audioSpec.silence = 0;
	  audioSpec.channels = pAudioCodeCtx->channels;
	  audioSpec.samples = SDL_AUDIO_BUFFER_SIZE;
	  audioSpec.callback = audio_callback;
	  audioSpec.userdata = pAudioCodeCtx;

	  if(SDL_OpenAudio(&audioSpec,&spec)<0){
		  printf("SDL_OpenAudio error:%s",SDL_GetError());
		  return -1;
	  }

	  packet_queue_init(&audioq);

	  SDL_PauseAudio(0);
      //����ͼ�񻺴�
	  pFrame = avcodec_alloc_frame();
	  if(pFrame == NULL)
		  return -1;

	  pFrameRGB = avcodec_alloc_frame();
	  if(pFrameRGB == NULL)
		  return -1;

	  numBytes = avpicture_get_size(PIX_FMT_RGB24,pCodecCtx->width,pCodecCtx->height);
	  buffer = av_malloc(numBytes);
	  avpicture_fill((AVPicture *)pFrameRGB,buffer,PIX_FMT_RGB24,
			  pCodecCtx->width,pCodecCtx->height);

	  i = 0;

	  // Allocate a place to put our YUV image on that screen
	 bmp = SDL_CreateYUVOverlay(pCodecCtx->width,
	  pCodecCtx->height,
	  SDL_YV12_OVERLAY,
	  screen);

	  while(av_read_frame(pFormatCtx,&packet)>=0){
		  process_events();
		  if(packet.stream_index == videoStream){
			  avcodec_decode_video2(pCodecCtx,pFrame,&frameFinished,&packet);
		  }else if(packet.stream_index == audioStream){
			  packet_queue_put(&audioq,&packet);
			  printf("queue nb_packets:%d\n",audioq.nb_packets);
		  }
		  if(frameFinished){
			  //SDL_LockYUVOverlay(bmp);

			  pFrameRGB->data[0] = bmp->pixels[0];
			  pFrameRGB->data[1] = bmp->pixels[2];
			  pFrameRGB->data[2] = bmp->pixels[1];

			  pFrameRGB->linesize[0] = bmp->pitches[0];
			  pFrameRGB->linesize[1] = bmp->pitches[2];
			  pFrameRGB->linesize[2] = bmp->pitches[1];

			  img_convert_ctx = sws_getCachedContext(img_convert_ctx,pCodecCtx->width,
			  					  pCodecCtx->height,pCodecCtx->pix_fmt,
			  					  pCodecCtx->width,pCodecCtx->height,
			  					PIX_FMT_YUV420P,SWS_BICUBIC,
			  					  NULL,NULL,NULL);

			  if(!img_convert_ctx){
				  fprintf(stderr,"Cannot initalize sws conversion context\n");
				  exit(1);
			  }

			  sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize,
			         0, pCodecCtx->height,pFrameRGB->data, pFrameRGB->linesize);

			  //SDL_UnlockYUVOverlay(bmp);

			  rect.x = 0;
			  rect.y = 0;
			  rect.w = pCodecCtx->width;
			  rect.h = pCodecCtx->height;
			  SDL_DisplayYUVOverlay(bmp, &rect);
			  if(i>100 && i < 150)
				  SaveFrame(pFrameRGB,pCodecCtx->width,pCodecCtx->height,i++);
			  else
				  i++;
			  SDL_Delay(20);

#if 0
			  struct SwsContext *img_convert_ctx = NULL;
			  img_convert_ctx = sws_getCachedContext(img_convert_ctx,pCodecCtx->width,
					  pCodecCtx->height,pCodecCtx->pix_fmt,
					  pCodecCtx->width,pCodecCtx->height,
					  PIX_FMT_RGB24,SWS_BICUBIC,
					  NULL,NULL,NULL);
			  if(!img_convert_ctx){
				  fprintf(stderr,"Cannot initalize sws conversion context\n");
				  exit(1);
			  }
			  sws_scale(img_convert_ctx,(const uint8_t* const*)pFrame->data,
					  pFrame->linesize,0,pCodecCtx->height,pFrameRGB->data,
					  pFrameRGB->linesize);
			  SaveFrame(pFrameRGB,pCodecCtx->width,pCodecCtx->height,i++);

			  /*
			  if(i++ < 50){
				  SaveAsBMP(pFrameRGB,pCodecCtx->width,pCodecCtx->height,i,24);
				  //SaveFrame(pFrameRGB,pCodecCtx->width,pCodecCtx->height,i);
			  }else{
				  break;
			  }
			  */
#endif
		  }
		  av_free_packet(&packet);
	  }
	  av_free(buffer);
	  av_free(pFrameRGB);
	  av_free(pFrame);
	  avcodec_close(pCodecCtx);
	  avformat_close_input(&pFormatCtx);
#endif
	  while(1){
		  process_events();
		  sleep(1);
	  }
	  cum_SDL_Quit();
	  return 0;
}

